New blog features, and framework changes
Posted on 2022-01-04 | Last updated 2022-01-04
|
Tags:
ProgrammingWeb-Development
For the past couple of months I have done a complete redesign of the blog, separating front-end and back-end completely, and rewriting it to use Svelte. In addition, I have added some new features. In this post I want to discuss the following:
Sveltekit is made as a framework in front of Svelte, and works like Nuxt or next, in that it provides SSG and SSR [1] support.
Although Svelte seems to be quite feature-complete, the same can't be said about Sveltekit, which while I was using it still is in early beta. I will now explain the problems I faced by rewriting the front-end using these two technologies.
As part of reactivity in Svelte it has invented a special syntax
I am very pleased with the result of the dynamic dropdown menu. It accomplishes everything I set out for it to do, and it does it using only CSS, while still allowing for unlimited recursion.
In the database all individual menu items are represented flatly, only containing the reference of their parent item. From a display perspective, generating that on the fly would normally be an O(N) operation; since you would need to create the structure by iterating over each item, and giving each parent item a reference to the child. This is what is done when editing.
At display time however, when a client requests the menu from the API it delivers a pregenerated nested JSON document skipping the O(N). This JSON is updated whenever a change happens to the menu.
Also here I met a problem with Svelte, which does not at the time of writing support proper recursion. Ideally you would want a menu like
To keep the google beast satisfied, I therefore abandoned my client-side rendering approach completely. This would allow google to index a static page with only HTML and CSS, and would therefore stop it using up precious time rendering the components on the client.
Ideally I would adopt a hybrid approach, where only clients identified as "bot" in their User-Agent would get Server-Side rendering, but there I met another problem, either with my understanding of Sveltekit, or with Javascript. Sveltekit needs you to pass in
Generally, the only problem I was faced with was how to pass variables into the front-end application, as it is built and then served. You would generally like to be able to change which API url is used, and not have it built into the image after all. One approach is to mount the file containing the changes in docker thereby replacing the one built into the image, but generally I have had problems getting this to work with front-end applications as they like to pack multiple files together, or replace them completely inline; and I am not familiar enough with the webpack solution Sveltekit uses, to know how to turn it off.
Sveltekit does support .env files, but only to be built into the application, not dynamically loaded at runtime, so even if I mounted a new .env it would not work without first building the application, which is the second approach. Create a start script that runs
A third option if you are using a node or another type of webserver is to pass the environment variables directly into the container, and use them directly from the application. Since I am runnning SSR, and not only serving files this was a possibility, and the path I went with. If you were using static site generation, or client side rendering, with Nginx or Apache to serve files, this would not work.
I have had a few problems with Sveltekit, but generally been able to accomplish what I set out to do.
The only thing I think I might want to add at some point is the ability to have posts in series. So this would be the second post in the design of my blog series. At the top of the post there would be a link to the previous, and the next posts.
- Svelte and Sveltekit
- Rewriting the blog front-end
- Adding new features
- Aliasing
- Dynamic menus
- SEO
- Docker
Svelte and Sveltekit
If you have not heard of Svelte, I would recommend watching this talk by the creator. What I found very interesting is that it does not use a virtual-dom like the other popular front-end frameworks, but rather compile everything down to plain javascript. This has the obvious benefit of reduced load-size, as the framework does not need to exist at runtime, and would in theory allow better performance since everything is compiled down to the basic javascript operations required to do a change.Sveltekit is made as a framework in front of Svelte, and works like Nuxt or next, in that it provides SSG and SSR [1] support.
Although Svelte seems to be quite feature-complete, the same can't be said about Sveltekit, which while I was using it still is in early beta. I will now explain the problems I faced by rewriting the front-end using these two technologies.
Rewriting the blog
I have experience with Vue, React, and now Svelte, and Svelte is definitely as easy to work with as Vue, and far ahead of React. If you want to have a central data repository, svelte has stores built in, which is something you would need Vuex or Redux to do in the other frameworks. But I have not even felt the need for a central data repository, as it is so simple to pass variables between components in Svelte.As part of reactivity in Svelte it has invented a special syntax
$:
. This functions like a fusion of Vue's "Computed" property, and general event subscription. Basically you can say "If this property changes, do this", or "This property should be recalculated if this".
// If the variable "visible" changes, reset property "name"
$: visible, (name = '');
Personally I am a fan of this, as it makes it a lot easier to cause changes in different components using the same data. I presume at scale it could cause major headaches due to anywhere being able to manipulate data, but in the limited scale of the types of front-end applications I write, I have not noticed any problems.
Adding new features
At the same time as moving to a new framework I added some features I wanted. The most critical was dynamic menus and aliasing; Specifically, I wanted to be able to have static posts, like an "About" page or a "Project" page; These pages should be able to not be in the post listing on the front page, and should get an url like iNOBStudios.com/About. At the same time said pages would need to be accessible from somewhere, therefore dynamic menus were needed.Dynamic menus
The menus are written using only CSS, and allow for unlimited recursion of submenus.
The menu editor interface
In the database all individual menu items are represented flatly, only containing the reference of their parent item. From a display perspective, generating that on the fly would normally be an O(N) operation; since you would need to create the structure by iterating over each item, and giving each parent item a reference to the child. This is what is done when editing.
At display time however, when a client requests the menu from the API it delivers a pregenerated nested JSON document skipping the O(N). This JSON is updated whenever a change happens to the menu.
Also here I met a problem with Svelte, which does not at the time of writing support proper recursion. Ideally you would want a menu like
<nav>
<ul>
<li><a href='/'>Home</a></li>
<li><a href='/About'>About</a></li>
<li>
<a>Projects</a>
<ul>
<li>
<a>Kingdom Falls</a>
<ul>
<li><a>Tower Defense</a></li>
<li><a>Untitled</a></li>
</ul>
</li>
<li><a href='/Post/5/Reflections on small-scale web services'>iNOBEngine</a></li>
</ul>
</li>
</ul>
</nav>
Since svelte did not support iterating through the nested JSON recursively, I was forced to generate the HTML manually as a string, and inject it into the component. Hopefully this will be added at some point, although I have not had use for it in my Vue or React projects, so I don't know if they support it.[2]
SEO
At my company, we have had a big focus on SEO lately, I had very little experience with it, and so after learning a lot I felt it necessary to add some things to the blog.Crawl budget
One of the things that surprised me the most was the concept of the "Crawl budget". That google has a limited amount of time it is willing to spend indexing your site. For this blog with only a couple posts it probably won't be a problem[3], but for my company websites, with millions of pages it might be.To keep the google beast satisfied, I therefore abandoned my client-side rendering approach completely. This would allow google to index a static page with only HTML and CSS, and would therefore stop it using up precious time rendering the components on the client.
Ideally I would adopt a hybrid approach, where only clients identified as "bot" in their User-Agent would get Server-Side rendering, but there I met another problem, either with my understanding of Sveltekit, or with Javascript. Sveltekit needs you to pass in
export const hydrate = false;
export const router = false;
to enable (or disable) SSR, and I cannot export a variable in an if, therefore doing a search on the User-Agent and conditionally enabling SSR does not seem to be an option in Sveltekit. Of course, it could be that the documentation is lacking, or that I am missing something with either Sveltekit or Javascript, but as of now it does not seem to be possible.
Parentless sites
Apparently google wants all routes in a site to have a link to be referenced somewhere. No page should be floating freely. To accomplish this i created https://inobstudios.com/Post which lists all the posts, and therefore serves as a hub site for all pages on iNOBStudios.com. Since I have static posts on the navbar, this should cover all public-facing sites.Sitemap
A sitemap.xml tells google which sites exist on your website, and when they were last updated, to (hopefully) allow it to prioritize where to use its crawl budget.OpenGraph
OpenGraph allows you to tell social media what they should display when something is linked. In my case it would be the title of the post, a short description, and the site logo, as my posts don't have logos. This is not directly SEO, but useful to know nonetheless.When linking a blog post on facebook, it shows the following based on the OpenGraph tags
Docker
Lastly, I converted my .NET Core API Backend, and Svelte Frontend into docker images, and set up a CI/(CD) process on github using github actions. When a change is commited to either application, a new image is generated and uploaded to the docker registry, which can then be pulled on the server.Generally, the only problem I was faced with was how to pass variables into the front-end application, as it is built and then served. You would generally like to be able to change which API url is used, and not have it built into the image after all. One approach is to mount the file containing the changes in docker thereby replacing the one built into the image, but generally I have had problems getting this to work with front-end applications as they like to pack multiple files together, or replace them completely inline; and I am not familiar enough with the webpack solution Sveltekit uses, to know how to turn it off.
Sveltekit does support .env files, but only to be built into the application, not dynamically loaded at runtime, so even if I mounted a new .env it would not work without first building the application, which is the second approach. Create a start script that runs
npm run build
on start of the application, but in general I am against that method as that means your docker image needs both the node application and the entire node_modules directory embedded[4].
A third option if you are using a node or another type of webserver is to pass the environment variables directly into the container, and use them directly from the application. Since I am runnning SSR, and not only serving files this was a possibility, and the path I went with. If you were using static site generation, or client side rendering, with Nginx or Apache to serve files, this would not work.
I have had a few problems with Sveltekit, but generally been able to accomplish what I set out to do.
The only thing I think I might want to add at some point is the ability to have posts in series. So this would be the second post in the design of my blog series. At the top of the post there would be a link to the previous, and the next posts.
[1] | Static Site Generation and Server-Side Rendering |
[2] | Although if you are familiar with PHP, Twig does support recursion, which I have found very useful in the past |
[3] | Unless they adjust the budget based on views on the site |
[4] | You could of course both install node, run npm install, run npm run build, and then remove node again, but that seems overly complicated. |